<template>
  <layout-content header="Nodes">
    <div style="float: left">
      <el-button
        v-has-permissions="{
          scope: 'cluster',
          apiGroup: '',
          resource: 'nodes',
          verb: 'update'
        }"
        type="primary"
        size="small"
        icon="el-icon-video-pause"
        @click="onHandleCordon(true)"
      >
        {{ $t("business.node.cordon") }}
      </el-button>
      <el-button
        v-has-permissions="{
          scope: 'cluster',
          apiGroup: '',
          resource: 'nodes',
          verb: 'update'
        }"
        type="primary"
        size="small"
        icon="el-icon-video-play"
        @click="onHandleCordon(false)"
      >
        {{ $t("business.node.uncordon") }}
      </el-button>
      <el-button
        v-has-permissions="{
          scope: 'cluster',
          apiGroup: '',
          resource: 'nodes',
          verb: 'update'
        }"
        type="primary"
        size="small"
        icon="el-icon-position"
        @click="onHandleDrain()"
      >
        {{ $t("business.node.drain") }}
      </el-button>
      <el-button
        type="primary"
        size="small"
        icon="el-icon-download"
        @click="onHandleExportExcel()"
      >
        {{ $t("commons.button.export") }}
      </el-button>
    </div>
    <complex-table
      v-model:selects="selections"
      v-loading="uiState.loading"
      :data="srcList"
      :pagination-config="query.paginationConfig"
      :search-config="query.searchConfig"
      table-layout="auto"
      @search="reloadResourceList"
    >
      <el-table-column type="selection" fix />
      <el-table-column
        :label="$t('commons.table.name')"
        prop="metadata.name"
        fix
        show-overflow-tooltip
        max-width="30px"
      >
        <template #default="{ row }">
          <el-button link type="primary" @click="onHandleViewDetails(row)">
            {{ row.metadata.name }}
          </el-button>
        </template>
      </el-table-column>

      <el-table-column
        label="Internal IP"
        prop="metadata.name"
        max-width="30px"
      >
        <template #default="{ row }">
          <div
            v-for="(address, index) in row.status.addresses"
            v-bind:key="index"
          >
            <span v-if="address.type === 'InternalIP'">
              {{ address.address }}
            </span>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="PodCIDR" prop="spec.podCIDR" />
      <el-table-column :label="$t('commons.table.status')">
        <template #default="{ row }">
          <el-space>
            <el-tag
              v-for="tag in getNodeStatus(row)"
              :key="tag.desc"
              :type="tag.type"
              >{{ tag.desc }}
            </el-tag>
          </el-space>
        </template>
      </el-table-column>
      <el-table-column
        :label="$t('business.node.role')"
        prop="metadata.labels"
        min-width="180px"
        show-overflow-tooltip
      >
        <template #default="{ row }">
          <el-space alignment="start">
            <div v-for="name in getNodeRoles(row)" :key="name">
              <el-tag>{{ name }}</el-tag>
            </div>
          </el-space>
        </template>
      </el-table-column>
      <el-table-column
        :label="$t('business.node.taints')"
        prop="metadata.labels"
        min-width="80px"
        show-overflow-tooltip
      >
        <template #default="{ row }">
          {{ row.spec.taints ? row.spec.taints.length + "" : "-" }}
        </template>
      </el-table-column>
      <el-table-column v-if="false" label="Pods" min-width="80px">
        <template #default="{ row }">
          {{ row.pods }}
        </template>
      </el-table-column>
      <el-table-column
        :label="'CPU ' + $t('business.node.usage')"
        min-width="80px"
      >
        <template #default="{ row }">
          <div v-if="operation.hasMetric">
            <div>
              <span>{{ getNodeUsage(row)?.cpuPercent }}%</span>
            </div>
            <div>
              <span>{{ getNodeUsage(row)?.cpuDesc || "-" }}</span>
            </div>
          </div>
          <div v-else>
            <div><span> - </span></div>
          </div>
        </template>
      </el-table-column>
      <el-table-column
        :label="$t('business.workload.memory') + $t('business.node.usage')"
        min-width="80px"
      >
        <template #default="{ row }">
          <div v-if="operation.hasMetric">
            <div>
              <span>{{ getNodeUsage(row)?.memoryPercent }}%</span>
            </div>
            <div>
              <span>{{ getNodeUsage(row)?.memoryDesc || "-" }}</span>
            </div>
          </div>
          <div v-else>
            <div><span> - </span></div>
          </div>
        </template>
      </el-table-column>
      <ko-table-operations
        :buttons="operation.buttons"
        :label="$t('commons.table.action')"
      />
    </complex-table>
  </layout-content>
</template>

<script setup lang="ts">
import LayoutContent from "@/views/domain/kubeHud/views/components/layout/LayoutContent.vue";
import ComplexTable from "@/views/domain/kubeHud/views/components/complex-table/index.vue";
import KoTableOperations from "@/views/domain/kubeHud/views/components/ko-table-operations/index.vue";
import writeXlsxFile from "write-excel-file";
import { useI18n } from "vue-i18n";
import {
  InjectLabelKey,
  NodeInfo,
  useK8SApi
} from "@/views/domain/kubeHud/api/k8s";
import { onMounted, reactive, ref } from "vue";
import {
  useBaseCRUDProvider,
  useBaseCRUDRoute
} from "@/views/domain/kubeHud/api/provider/baseListProvider";
import { checkPermissions } from "@/views/domain/kubeHud/utils/permission";
import { KRouteNode } from "@/views/domain/kubeHud/router";
import { ElMessage } from "element-plus";
import { SafePromiseAll } from "@/utils/promise";
import { fieldSelectorStr } from "@/views/domain/kubeHud/utils";
import {
  MetricUsage,
  NodeMetric
} from "@/views/domain/kubeHud/api/k8s/metricType";
import {
  cpuUnitConvert,
  getCpuCores,
  getCpuCoresStr,
  giMemoryFormat,
  mCpuFormat,
  mCpuFormatFixed,
  memoryUnitConvert,
  miMemoryFormat
} from "@/views/domain/kubeHud/utils/unitConvert";

// import { cordonNode, listNodes } from "@/api/nodes";
// import { evictionPod, listPodsWithNsSelector } from "@/api/pods";
// import { checkPermissions } from "@/utils/permission";
// import { listNodeMetrics } from "@/api/apis";
// import { cpuUnitConvert, memoryUnitConvert } from "@/utils/unitConvert";

const { t } = useI18n();
const { nodeRepo, podRepo } = useK8SApi();

type Prop = {};
const props = defineProps<Prop>();

const {
  selections,
  srcList,
  query,
  router,
  uiState,
  downloadSourceAsYaml,
  getResourceDetails,
  getResourceList,
  updateResource,
  deleteResource,
  confirmDelete,
  reloadResourceList
} = useBaseCRUDProvider(
  {
    request: {
      getDetailsReq: nodeRepo.getNodeDetail,
      getListReq: nodeRepo.getNodeList,
      updateReq: nodeRepo.updateNode,
      deleteReq: nodeRepo.deleteNode,
      createReq: nodeRepo.createNode
    }
  },
  false
);

const { pushTo, openBlank } = useBaseCRUDRoute({ router });

type CustomUsage = {
  cpu?: number;
  cpuPercent?: number;
  cpuDesc?: string;
  memory?: number;
  memoryPercent?: number;
  memoryDesc?: string;
};
const nodeUsage = ref<Record<string, CustomUsage>>({});

const operation = reactive({
  hasMetric: false,
  buttons: [
    {
      label: t("commons.button.edit"),
      icon: "el-icon-edit",
      click: row => {
        pushTo(KRouteNode.Edit, { name: row.metadata.name });
      },
      disabled: () => {
        return !checkPermissions({
          scope: "cluster",
          apiGroup: "",
          resource: "nodes",
          verb: "update"
        });
      }
    },
    {
      label: t("commons.button.view_yaml"),
      icon: "el-icon-view",
      click: row => {
        pushTo(KRouteNode.Edit, { name: row.metadata.name, yamlShow: "true" });
      }
    },
    {
      label: "Shell",
      icon: "iconfont iconline-terminalzhongduan",
      disabled: () => {
        return !checkPermissions({
          scope: "namespace",
          apiGroup: "",
          resource: "pods/exec",
          verb: "create"
        });
      },
      click: row => {
        openBlank(KRouteNode.Terminal, {
          namespace: row.metadata.namespace,
          nodeName: row.metadata.name
        });
      }
    }
  ]
});

// 获取节点角色
const getNodeRoles = (row: NodeInfo): string[] => {
  return Object.keys(row.metadata?.labels ?? {})
    .filter(
      item =>
        item.startsWith(InjectLabelKey.NodeRole) &&
        `${row.metadata.labels[item]}` === "true"
    )
    .map(item => item.replace(InjectLabelKey.NodeRole + "/", ""));
};

// 获取节点状态
const getNodeStatus = (row: NodeInfo): { desc: string; type: any }[] => {
  const ready = row.status.conditions?.find(
    item => item.type === "Ready" && item.status === "True"
  );
  const schedulable = !row.spec.unschedulable;
  if (ready && schedulable) {
    return [
      {
        desc: "Running",
        type: "success"
      }
    ];
  }
  const status = [
    { desc: ready ? "Ready" : "NotReady", type: ready ? "success" : "danger" },
    {
      desc: row.spec.unschedulable ? "Cordoned" : "Active",
      type: row.spec.unschedulable ? "warning" : "success"
    }
  ];
  return status;
};

const getNodeUsage = (row: NodeInfo): CustomUsage | undefined => {
  return nodeUsage.value[row.metadata.name];
};

const onHandleViewDetails = (row: NodeInfo) => {
  pushTo(KRouteNode.Details, { name: row.metadata.name });
};

const onHandleCreate = (yaml?: boolean) => {
  pushTo(KRouteNode.Create, { yaml: yaml ? "true" : "false" });
};

const checkIsValid = (node?: NodeInfo, isCordon?: boolean) => {
  if (isCordon) {
    if (node && node.spec.unschedulable) {
      return false;
    } else {
      for (const item of selections.value) {
        if (item.spec.unschedulable) {
          ElMessage.error({ message: t("business.node.existing_cordoned") });
          return false;
        }
      }
    }
  } else {
    if (node && !node.spec.unschedulable) {
      return false;
    } else {
      for (const item of selections.value) {
        if (!item.spec.unschedulable) {
          ElMessage.error({ message: t("business.node.existing_actived") });
          return false;
        }
      }
    }
  }
  return true;
};

const onHandleCordon = async (
  isCordon: boolean,
  node?: NodeInfo,
  mention: boolean = true
) => {
  if (!checkIsValid(node, isCordon)) {
    return;
  }
  const res = await SafePromiseAll(
    selections.value.map(item => nodeRepo.cordonNode({}, item, isCordon))
  );

  if (res.every(item => item.ok)) {
    mention &&
      ElMessage.success({ message: t("commons.msg.operation_success") });
    selections.value = [];
    return true;
  } else {
    mention && ElMessage.error({ message: t("commons.msg.operation_failed") });
    return false;
  }
};

// 停止node
const onHandleDrain = async (node?: NodeInfo) => {
  // 停止所有node调度
  const selectedNodes = [...new Set(selections.value ?? [])];
  if (await onHandleCordon(true, node, false)) {
    ElMessage.error({ message: t("commons.msg.operation_failed") });
    return;
  }

  if (
    node &&
    selectedNodes.findIndex(item => item.metadata.name === node.metadata.name) <
      0
  ) {
    selectedNodes.push(node);
  }

  // 驱逐pod
  const res = await SafePromiseAll(
    selectedNodes.map(item => evictNodePods(item, false))
  );
  if (res.every(item => item)) {
    ElMessage.success({ message: t("commons.msg.operation_success") });
    return true;
  } else {
    ElMessage.error({ message: t("commons.msg.operation_failed") });
  }
  return false;
};

//TODO: 效率存疑
// 驱逐某节点下的pods
const evictNodePods = async (node?: NodeInfo, mention: boolean = true) => {
  const { items } = await podRepo.getPodList({
    query: {
      fieldSelector: fieldSelectorStr({
        "spec.nodeName": node?.metadata.name
      })
    }
  });

  const res = await SafePromiseAll(
    items.map(pod =>
      podRepo.evictionPod({
        apiVersion: "policy/v1beta1",
        kind: "Eviction",
        metadata: {
          name: pod.metadata.name,
          namespace: pod.metadata.namespace
        },
        deleteOptions: {}
      })
    )
  );
  if (res.every(item => item.ok)) {
    mention &&
      ElMessage.success({ message: t("commons.msg.operation_success") });
    return true;
  } else {
    mention && ElMessage.error({ message: t("commons.msg.operation_failed") });
  }

  return false;
};

const onHandleExportExcel = () => {};

const reloadNodeMetrics = async () => {
  nodeRepo
    .getNodeMetrics({})
    .then(({ items }) => {
      if (items) {
        operation.hasMetric = true;
        nodeUsage.value = {};
        items.forEach(element => {
          const usage: CustomUsage = {
            cpu: getCpuCores(element.usage.cpu),
            memory: memoryUnitConvert(element.usage.memory)
          };

          // target node
          const node = srcList.value.find(
            item => item.metadata.name === element.metadata.name
          );
          if (node && node.status && node.status.allocatable) {
            usage.cpuDesc = `${getCpuCoresStr(element.usage.cpu)} / ${node.status.allocatable.cpu} ${t("business.node.core")}`;

            const cpuAlloc = node.status.allocatable.cpu;
            usage.cpuPercent = Math.round((usage.cpu / cpuAlloc) * 100);

            const memoryAlloc = memoryUnitConvert(
              node.status.allocatable.memory
            );
            usage.memoryDesc = `${(usage.memory / 1024).toFixed(2)} / ${(memoryAlloc / 1024).toFixed(2)} Gi`;
            usage.memoryPercent = Math.round(
              (usage.memory / memoryAlloc) * 100
            );
          }

          nodeUsage.value[element.metadata.name] = usage;
        });
      } else {
        operation.hasMetric = false;
      }
    })
    .catch(e => {
      console.log(e);
      operation.hasMetric = false;
    });
};

onMounted(() => {
  reloadResourceList().then(() => reloadNodeMetrics());
});
</script>

<style scoped></style>
